import asyncio
import pygame
import edge_tts
import tkinter as tk
import win32gui
import win32con
import threading
import hashlib
import os
import json

def read_config():
    with open(file="config.json", mode="r", encoding="utf-8") as f:
        config = json.loads(f.read())
    with open(file="fixed_collocation.json", mode="r", encoding="utf-8") as f:
        fixed_collocation = json.loads(f.read())
    return config, fixed_collocation

config, fixed_collocation = read_config()

VOICE = config["voice"]
path = config["save_path"]
rate = config["rate"]
pitch = config["pitch"]

def search_fixed_collocation(TEXT):
    for collocation in fixed_collocation:
        if collocation["before"] == TEXT:
            return collocation["after"]
    return TEXT

def set_topmost():
    hwnd = win32gui.FindWindow(None, root.title())
    win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)

root = tk.Tk()
root.geometry("400x200")
root.title("tts")
# root.after(100, set_topmost)
lock = threading.Lock()

def get_savepath(TEXT):
    encoded_text = TEXT.encode('utf-8')
    hash_object = hashlib.sha256(encoded_text)
    hash_value = hash_object.hexdigest()
    return f"{path}/{hash_value}.mp3"

async def speak(TEXT):
    event = asyncio.Event()
    try:
        communicate = edge_tts.Communicate(TEXT, VOICE, rate = rate, pitch = pitch)
        await communicate.save(get_savepath(TEXT))
        event.set()
        return event
    except Exception as e:
        print(f"An error occurred during TTS: {e}")
        if os.path.exists(get_savepath(TEXT)):
            os.remove(get_savepath(TEXT))
        return None

# 创建一个Frame来容纳文本框
frame = tk.Frame(root)
frame.place(relx = 0.5, rely = 0.5, anchor = 'center')
warn_label = tk.Label(frame, text="输入有误无法播放")
warn_label.pack_forget()
# 创建一个文本输入框
prompt_label = tk.Label(frame, text="在此输入语音")
prompt_label.pack()
text_entry = tk.Entry(frame)
text_entry.pack()

def clear_memory(path):
    for file in os.listdir(path):
        file_path = os.path.join(path, file)
        if os.path.isfile(file_path):
            os.remove(file_path)

def update_config():
    global VOICE
    global path
    global rate
    global pitch
    global config
    global fixed_collocation
    config, fixed_collocation = read_config()
    VOICE = config["voice"]
    path = config["save_path"]
    rate = config["rate"]
    pitch = config["pitch"]
    clear_memory(path)
    

# button = tk.Button(frame, text="更新配置文件，该操作会清空缓存", command=update_config)
# button.pack()

def play_mp3(TEXT):
    lock.acquire()
    try:
        pygame.mixer.init()
        pygame.mixer.music.load(get_savepath(TEXT))
        pygame.mixer.music.play()
        while pygame.mixer.music.get_busy():
            pygame.time.Clock().tick(10)
        warn_label.pack_forget()
        pygame.mixer.quit()
    except Exception as e:
        warn_label.pack()
    finally:
        lock.release()


def tts(TEXT):
    save_path = get_savepath(TEXT)
    if not os.path.exists(save_path):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        event = loop.run_until_complete(speak(TEXT))
        if None == event:
            warn_label.pack()
            return
        loop.run_until_complete(event.wait())
    threading.Thread(target = play_mp3, args=(TEXT,)).start()
    
def submit_text():
    if '' == text_entry.get():
        return
    TEXT = search_fixed_collocation(text_entry.get())
    threading.Thread(target = tts, args=(TEXT,)).start()
    text_entry.delete(0, tk.END)

text_entry.bind('<Return>', lambda event: submit_text())
root.mainloop()